﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace AndrysNet.HiUpdateTools_Addin.Core
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class MapiMessage
    {
        public int reserved;
        public string subject;
        public string noteText;
        public string messageType;
        public string dateReceived;
        public string conversationID;
        public int flags;
        public IntPtr originator;
        public int recipCount;
        public IntPtr recips;
        public int fileCount;
        public IntPtr files;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class MapiRecipDesc
    {
        public int reserved;
        public int recipClass;
        public string name;
        public string address;
        public int eIDSize;
        public IntPtr entryID;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class MapiFileDesc
    {
        public int reserved;
        public int flags;
        public int position;
        public string pathName;
        public string fileName;
        public IntPtr fileType;
    }
    public class MAPI
    {
        #region static
        const int MapiLogonUI = 0x00000001;
        const int MAPI_DIALOG = 0x8;
        [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)]
        private static extern int MAPISendMail(IntPtr session, IntPtr uiParam,
            MapiMessage message, int flags, int reserved);
        [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)]
        private static extern int MAPILogon(IntPtr hwnd, string profileName, string password,
            int flags, int reserved, ref IntPtr session);
        [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)]
        private static extern int MAPILogoff(IntPtr session, IntPtr hwnd,
            int flags, int reserved);
        static IntPtr OffsetPtr(IntPtr ptr, Type structureType, int offset)
        {
            return (IntPtr)((long)ptr + offset * Marshal.SizeOf(structureType));
        }
        #endregion
        string[] files;
        string subject = String.Empty;
        string body = String.Empty;
        string recipentName = String.Empty;
        string recipientAddress = String.Empty;
        IntPtr handle = IntPtr.Zero;
        IntPtr session = IntPtr.Zero;
        int error;
        public MAPI(IntPtr handle, string[] files, string mailSubject, string mailBody, string recipentName, string recipientAddress)
        {
            this.files = files;
            this.subject = mailSubject;
            this.body = mailBody;
            this.recipentName = recipentName;
            this.recipientAddress = recipientAddress;
            if (Logon(handle))
            {
                MapiMessage msg = CreateMessage();
                error = MAPISendMail(session, handle, msg, MAPI_DIALOG, 0);
                Logoff();
                DisposeMessage(msg);
            }
        }
        MapiMessage CreateMessage()
        {
            MapiMessage msg = new MapiMessage();
            msg.subject = subject;
            msg.noteText = body;
            if (files.Length > 0)
            {
                msg.fileCount = files.Length;
                msg.files = GetFilesDesc();
            }
            if (!string.IsNullOrEmpty(recipentName) || !string.IsNullOrEmpty(recipientAddress))
            {
                msg.recipCount = recipientAddress.Split(";".ToArray()).Count();

                msg.recips = GetRecipDesc();
            }
            return msg;
        }

        IntPtr GetFilesDesc()
        {
            IntPtr ptra = AllocMemory(typeof(MapiFileDesc), files.Length);
            for (int i = 0; i < files.Length; i++)
            {
                MapiFileDesc fileDesc = new MapiFileDesc();
                fileDesc.position = i;
                fileDesc.fileName = Path.GetFileName(files[i]);
                fileDesc.pathName = files[i];
                Marshal.StructureToPtr(fileDesc, OffsetPtr(ptra, typeof(MapiFileDesc), files.Length - 1 - i), false);
            }
            return ptra;
        }
        IntPtr GetRecipDesc()
        {
            string[] adress = recipientAddress.Split(";".ToCharArray());
            IntPtr ptra = AllocMemory(typeof(MapiRecipDesc), adress.Length);

            for (int i = 0; i < adress.Length; i++)
            {
                MapiRecipDesc recipDesc = new MapiRecipDesc();

                recipDesc.reserved = 0;
                const int MAPI_TO = 1;
                recipDesc.recipClass = MAPI_TO;
                recipDesc.name = adress[i].Trim();
                ///  recipDesc.address = adress[i].Trim();
                recipDesc.eIDSize = 0;

                recipDesc.entryID = IntPtr.Zero;
                ///   Marshal.StructureToPtr(recipDesc, (IntPtr)ptra, false);
                Marshal.StructureToPtr(recipDesc, OffsetPtr(ptra, typeof(MapiRecipDesc), adress.Length - 1 - i), false);
            }



            return ptra;
        }
        IntPtr AllocMemory(Type structureType, int count)
        {
            return Marshal.AllocHGlobal(Marshal.SizeOf(structureType) * count);
        }
        void Logoff()
        {
            if (session != IntPtr.Zero)
            {
                error = MAPILogoff(session, handle, 0, 0);
                session = IntPtr.Zero;
            }
        }
        bool Logon(IntPtr hwnd)
        {
            this.handle = hwnd;
            error = MAPILogon(hwnd, null, null, 0, 0, ref session);
            if (error != 0)
                error = MAPILogon(hwnd, null, null, MapiLogonUI, 0, ref session);
            return error == 0;
        }
        void DisposeMessage(MapiMessage msg)
        {
            FreeMemory(msg.files, typeof(MapiFileDesc), files.Length);
            FreeMemory(msg.recips, typeof(MapiRecipDesc), 1);
            msg = null;
        }
        void FreeMemory(IntPtr ptr, Type structureType, int count)
        {
            if (ptr != IntPtr.Zero)
            {
                for (int i = 0; i < count; i++)
                {
                    Marshal.DestroyStructure(OffsetPtr(ptr, structureType, i), structureType);
                }
                Marshal.FreeHGlobal(ptr);
            }
        }

    }
}
